/* Copyright 2010  Jacob Brunson  (email : wp-jacob.gpl.license@jacobbrunson.com )

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

var G_DOCS_FILE_EXTENSIONS = { 
    '.csv': 'text/csv', 
    '.doc': 'application/msword', 
    '.htm': 'text/html', 
    '.html': 'text/html', 
    '.ods': 'application/x-vnd.oasis.opendocument.spreadsheet', 
    '.odt': 'application/vnd.oasis.opendocument.text', 
    '.pdf': 'application/pdf', 
    '.pps': 'application/vnd.ms-powerpoint', 
    '.ppt': 'application/vnd.ms-powerpoint', 
    '.rtf': 'application/rtf', 
    '.sxw': 'application/vnd.sun.xml.writer', 
    '.tab': 'text/tab-separated-values', 
    '.txt': 'text/plain', 
    '.tsv': 'text/tab-separated-values', 
    '.xls': 'application/vnd.ms-excel',
    '.docx':'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
    '.xlsx':'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
};

var COMMON_FILE_EXTENSIONS = {
    '.323': 'text/h323',
    '.acx': 'application/internet-property-stream',
    '.ai' : 'application/postscript',
    '.aif': 'audio/x-aiff',
    '.aifc': 'audio/x-aiff',
    '.aiff': 'audio/x-aiff',
    '.asf': 'video/x-ms-asf',
    '.asr': 'video/x-ms-asf',
    '.asx': 'video/x-ms-asf',
    '.au' : 'audio/basic',
    '.avi': 'video/x-msvideo',
    '.axs': 'application/olescript',
    '.bas': 'text/plain',
    '.bcpio': 'application/x-bcpio',
    '.bin': 'application/octet-stream',
    '.bmp': 'image/bmp',
    '.c'  : 'text/plain',
    '.cat': 'application/vnd.ms-pkiseccat',
    '.cdf': 'application/x-cdf',
    '.cer': 'application/x-x509-ca-cert',
    '.class': 'application/octet-stream',
    '.clp': 'application/x-msclip',
    '.cmx': 'image/x-cmx',
    '.cod': 'image/cis-cod',
    '.cpio': 'application/x-cpio',
    '.crd': 'application/x-mscardfile',
    '.crl': 'application/pkix-crl',
    '.crt': 'application/x-x509-ca-cert',
    '.csh': 'application/x-csh',
    '.css': 'text/css',
    '.dcr': 'application/x-director',
    '.der': 'application/x-x509-ca-cert',
    '.dir': 'application/x-director',
    '.dll': 'application/x-msdownload',
    '.dms': 'application/octet-stream',
    '.doc': 'application/msword',
    '.dot': 'application/msword',
    '.dvi': 'application/x-dvi',
    '.dxr': 'application/x-director',
    '.eps': 'application/postscript',
    '.etx': 'text/x-setext',
    '.evy': 'application/envoy',
    '.exe': 'application/octet-stream',
    '.fif': 'application/fractals',
    '.flr': 'x-world/x-vrml',
    '.gif': 'image/gif',
    '.gtar': 'application/x-gtar',
    '.gz' : 'application/x-gzip',
    '.h'  : 'text/plain',
    '.hdf': 'application/x-hdf',
    '.hlp': 'application/winhlp',
    '.hqx': 'application/mac-binhex40',
    '.hta': 'application/hta',
    '.htc': 'text/x-component',
    '.htm': 'text/html',
    '.html': 'text/html',
    '.htt': 'text/webviewhtml',
    '.ico': 'image/x-icon',
    '.ief': 'image/ief',
    '.iii': 'application/x-iphone',
    '.ins': 'application/x-internet-signup',
    '.isp': 'application/x-internet-signup',
    '.jfif': 'image/pipeg',
    '.jpe': 'image/jpeg',
    '.jpeg': 'image/jpeg',
    '.jpg': 'image/jpeg',
    '.js' : 'application/x-javascript',
    '.latex': 'application/x-latex',
    '.lha': 'application/octet-stream',
    '.lsf': 'video/x-la-asf',
    '.lsx': 'video/x-la-asf',
    '.lzh': 'application/octet-stream',
    '.m13': 'application/x-msmediaview',
    '.m14': 'application/x-msmediaview',
    '.m3u': 'audio/x-mpegurl',
    '.man': 'application/x-troff-man',
    '.mdb': 'application/x-msaccess',
    '.me' : 'application/x-troff-me',
    '.mht': 'message/rfc822',
    '.mhtml': 'message/rfc822',
    '.mid': 'audio/mid',
    '.mny': 'application/x-msmoney',
    '.mov': 'video/quicktime',
    '.movie': 'video/x-sgi-movie',
    '.mp2': 'video/mpeg',
    '.mp3': 'audio/mpeg',
    '.mpa': 'video/mpeg',
    '.mpe': 'video/mpeg',
    '.mpeg': 'video/mpeg',
    '.mpg': 'video/mpeg',
    '.mpp': 'application/vnd.ms-project',
    '.mpv2': 'video/mpeg',
    '.ms' : 'application/x-troff-ms',
    '.mvb': 'application/x-msmediaview',
    '.nws': 'message/rfc822',
    '.oda': 'application/oda',
    '.p10': 'application/pkcs10',
    '.p12': 'application/x-pkcs12',
    '.p7b': 'application/x-pkcs7-certificates',
    '.p7c': 'application/x-pkcs7-mime',
    '.p7m': 'application/x-pkcs7-mime',
    '.p7r': 'application/x-pkcs7-certreqresp',
    '.p7s': 'application/x-pkcs7-signature',
    '.pbm': 'image/x-portable-bitmap',
    '.pdf': 'application/pdf',
    '.pfx': 'application/x-pkcs12',
    '.pgm': 'image/x-portable-graymap',
    '.pko': 'application/ynd.ms-pkipko',
    '.pma': 'application/x-perfmon',
    '.pmc': 'application/x-perfmon',
    '.pml': 'application/x-perfmon',
    '.pmr': 'application/x-perfmon',
    '.pmw': 'application/x-perfmon',
    '.png': 'image/png',
    '.pnm': 'image/x-portable-anymap',
    '.pot': 'application/vnd.ms-powerpoint',
    '.ppm': 'image/x-portable-pixmap',
    '.pps': 'application/vnd.ms-powerpoint',
    '.ppt': 'application/vnd.ms-powerpoint',
    '.prf': 'application/pics-rules',
    '.ps' : 'application/postscript',
    '.pub': 'application/x-mspublisher',
    '.qt' : 'video/quicktime',
    '.ra' : 'audio/x-pn-realaudio',
    '.ram': 'audio/x-pn-realaudio',
    '.ras': 'image/x-cmu-raster',
    '.rgb': 'image/x-rgb',
    '.rmi': 'audio/mid',
    '.roff': 'application/x-troff',
    '.rtf': 'application/rtf',
    '.rtx': 'text/richtext',
    '.scd': 'application/x-msschedule',
    '.sct': 'text/scriptlet',
    '.setpay': 'application/set-payment-initiation',
    '.setreg': 'application/set-registration-initiation',
    '.sh' : 'application/x-sh',
    '.shar': 'application/x-shar',
    '.sit': 'application/x-stuffit',
    '.snd': 'audio/basic',
    '.spc': 'application/x-pkcs7-certificates',
    '.spl': 'application/futuresplash',
    '.src': 'application/x-wais-source',
    '.sst': 'application/vnd.ms-pkicertstore',
    '.stl': 'application/vnd.ms-pkistl',
    '.stm': 'text/html',
    '.svg': 'image/svg+xml',
    '.sv4cpio': 'application/x-sv4cpio',
    '.sv4crc': 'application/x-sv4crc',
    '.t'  : 'application/x-troff',
    '.tar': 'application/x-tar',
    '.tcl': 'application/x-tcl',
    '.tex': 'application/x-tex',
    '.texi': 'application/x-texinfo',
    '.texinfo': 'application/x-texinfo',
    '.tgz': 'application/x-compressed',
    '.tif': 'image/tiff',
    '.tiff': 'image/tiff',
    '.tr' : 'application/x-troff',
    '.trm': 'application/x-msterminal',
    '.tsv': 'text/tab-separated-values',
    '.txt': 'text/plain',
    '.uls': 'text/iuls',
    '.ustar': 'application/x-ustar',
    '.vcf': 'text/x-vcard',
    '.vrml': 'x-world/x-vrml',
    '.wav': 'audio/x-wav',
    '.wcm': 'application/vnd.ms-works',
    '.wdb': 'application/vnd.ms-works',
    '.wks': 'application/vnd.ms-works',
    '.wmf': 'application/x-msmetafile',
    '.wps': 'application/vnd.ms-works',
    '.wri': 'application/x-mswrite',
    '.wrl': 'x-world/x-vrml',
    '.wrz': 'x-world/x-vrml',
    '.xaf': 'x-world/x-vrml',
    '.xbm': 'image/x-xbitmap',
    '.xla': 'application/vnd.ms-excel',
    '.xlc': 'application/vnd.ms-excel',
    '.xlm': 'application/vnd.ms-excel',
    '.xls': 'application/vnd.ms-excel',
    '.xlt': 'application/vnd.ms-excel',
    '.xlw': 'application/vnd.ms-excel',
    '.xof': 'x-world/x-vrml',
    '.xpm': 'image/x-xpixmap',
    '.xwd': 'image/x-xwindowdump',
    '.z'  : 'application/x-compress',
    '.zip': 'application/zip'  
};

var DEFAULT_MIME_TYPE = "application/octet-stream";

function utf8_encode(string) {
    string = string.replace(/\r\n/g,"\n");
    var utftext = "";

    for (var n = 0; n < string.length; n++) {

        var c = string.charCodeAt(n);

        if (c < 128) {
            utftext += String.fromCharCode(c);
        }
        else if((c > 127) && (c < 2048)) {
            utftext += "%" + ((c >> 6) | 192).toString(16);
            utftext += "%" + ((c & 63) | 128).toString(16);
        }
        else {
            utftext += "%" + ((c >> 12) | 224).toString(16);
            utftext += "%" + (((c >> 6) & 63) | 128).toString(16);
            utftext += "%" + ((c & 63) | 128).toString(16);
        }

    }

    return utftext;
}

function getMimeType(file_ext)
{
    if (G_DOCS_FILE_EXTENSIONS[file_ext.toLowerCase()]) {
        return G_DOCS_FILE_EXTENSIONS[file_ext.toLowerCase()];
    } else if (COMMON_FILE_EXTENSIONS[file_ext.toLowerCase()]) {
        return COMMON_FILE_EXTENSIONS[file_ext.toLowerCase()]
    }
    return DEFAULT_MIME_TYPE;
}

function GdataFileUploader(file_obj, auth_obj, progress_callback, finished_callback, error_callback)
{
    var that_gdata_obj = this;
    this.fstream = null;
    this.file_obj = file_obj;
    this.file_size = file_obj.size;
    this.sock = new air.Socket();
    this.headerdata = "";
    this.contentdata = "";
    this.proxy_host = "";
    this.proxy_port = "";
    this.use_proxy = false;
    this.file_obj = file_obj;
    this.auth_obj = auth_obj;
    this.progress_callback = progress_callback;
    this.finished_callback = finished_callback;
    this.error_callback = error_callback;
    this.receive_mode = 1;
    this.total_bytes_sent = 0;
    
    this.setProxy = function (hostname, port)
    {
        air.trace("Using proxy", hostname, proxy);
        this.proxy_host = hostname;
        this.proxy_port = port;
        this.use_proxy = true;
    }
    
    this.receiveSocketData = function (event)
    {
        if (this.receive_mode == 1) {
            this.headerdata += this.sock.readUTFBytes(event.bytesLoaded);
            splits = this.headerdata.split("\r\n\r\n");
            if (splits.length == 2) {
                this.headerdata = splits[0];
                air.trace(this.headerdata);
                this.contentdata = splits[1];
                this.receive_mode = 2;
            }
        } else {
            this.contentdata += this.sock.readUTFBytes(event.bytesLoaded);
        }
    }
    
    this.sendHTTPHeader = function ()
    {
        air.trace("Socket opened");
        if (this.use_proxy) {
            this.sock.writeUTFBytes("POST http://docs.google.com/feeds/default/private/full HTTP/1.0\r\n");
        } else {
            this.sock.writeUTFBytes("POST /feeds/default/private/full HTTP/1.0\r\n");
        }
        this.sock.writeUTFBytes("Host: docs.google.com\r\n");
        this.sock.writeUTFBytes(this.auth_obj.getAuthHeader()+"\r\n");
        this.sock.writeUTFBytes("GData-Version: 3.0\r\n");
        air.trace("å", utf8_encode("å"));
        air.trace("Encoded filename", utf8_encode(this.file_obj.name));
        this.sock.writeUTFBytes("Slug: "+utf8_encode(this.file_obj.name)+"\r\n");
        this.sock.writeUTFBytes("Content-Length: "+this.file_obj.size+"\r\n");
        if (this.file_obj.type != null) {
            mimetype = getMimeType(this.file_obj.type);
        } else {
            mimetype = getMimeType("."+this.file_obj.extension)
        } 
        air.trace('mime type is', mimetype)
        this.sock.writeUTFBytes("Content-Type: "+mimetype+"\r\n");
        this.sock.writeUTFBytes("\r\n");
        air.trace("Wrote POST header");
    }
    
    this.sendPayload = function ()
    {
        this.fstream = new air.FileStream();
        air.trace(this.fstream, this.file_obj);
        this.fstream.addEventListener(air.ProgressEvent.PROGRESS, function(event){that_gdata_obj.readDataReady(event);});
        this.fstream.addEventListener(air.Event.COMPLETE, function(event){that_gdata_obj.doneSending(event);})
        air.trace("opening async stream", this.fstream);
        this.fstream.openAsync(this.file_obj, air.FileMode.READ);
        air.trace("filestream should be opened");
    }
    
    this.readDataReady = function(event)
    {
        timer = new air.Timer(1, 1);
        timer.addEventListener(air.TimerEvent.TIMER, function(event){that_gdata_obj.sendMore(event);});
        timer.start();
    }

    this.sendMore = function(event)
    {
        n = 0;
        while ((this.file_size - this.total_bytes_sent) > 0 && n < 512 && this.fstream.bytesAvailable > 0) {
            bite = this.fstream.readByte();
            this.sock.writeByte(bite);
            this.total_bytes_sent++;
            n++;
        }
        this.sock.flush();
        this.progress_callback(this.total_bytes_sent/this.file_size);
        if ((this.file_size - this.total_bytes_sent) > 0 && this.fstream.bytesAvailable > 0) {
            event.target.reset();
            event.target.start();
        }
    }
    
    this.doneSending = function(event)
    {
        if (this.fstream.bytesAvailable > 0) {
            setTimeout(function(that,event){that.doneSending(event);}, 10, this, event);
        } else {
            air.trace("Completed read of file, upload", event, event.target);
            event.target.close();
            this.progress_callback(1);
        }
    }
    
    this.socket_communication = function (event)
    {
        this.sendHTTPHeader();
        this.sendPayload();
    }
    
    this.done = function (event)
    {
        //TODO: evaluate server's HTTP response (should be 201)
        air.trace("Socket closed");
        parser=new DOMParser();
        xmldocument = parser.parseFromString(this.contentdata, "text/xml");
        linktags = xmldocument.getElementsByTagName('link');
        documenturl = null;
        for (i = 0; i < linktags.length; i++) {
            if (linktags[i].attributes.getNamedItem('rel').nodeValue == 'alternate') {
                air.trace("link to document is", linktags[i].attributes.getNamedItem('href').nodeValue);
                documenturl = linktags[i].attributes.getNamedItem('href').nodeValue;
                break;
            }
        }
        if (documenturl == null) {
            reasons = xmldocument.getElementsByTagName('internalReason');
            air.trace('Google Error:', reasons[0].textContent);
            if (this.error_callback != null) {
                this.error_callback("Google returned an error: "+reasons[0].textContent);
            }
        } else if (this.finished_callback != null) {
            this.finished_callback(documenturl);
        }
    }
    
    this.handleerror = function (event)
    {
        air.trace("An error occurred:"+event.type);
        if (event.type == "securityError") {
            air.trace(event.toString());
        }
    }
    
    this.upload = function ()
    {
        if (this.file_obj.size > 1000000000) {
            this.error_callback("This file is too large to upload");
                return;
        }
        
        if (this.file_obj.isDirectory) {
            this.error_callback("Directories cannot be uploaded.");
            return;
        }
        
        if (!this.file_obj.exists) {
            this.error_callback("This file does not exist");
            return;
        }
        
        this.sock.addEventListener(air.ProgressEvent.SOCKET_DATA, 
                                   function(event){that_gdata_obj.receiveSocketData(event);});
        this.sock.addEventListener(air.Event.CONNECT, 
                                   function(event){that_gdata_obj.socket_communication(event);});
        this.sock.addEventListener(air.Event.CLOSE, 
                                   function(event) {that_gdata_obj.done(event);});
        this.sock.addEventListener(air.IOErrorEvent.IO_ERROR, 
                                   function(event) {that_gdata_obj.handleerror(event);});
        this.sock.addEventListener(air.SecurityErrorEvent.SECURITY_ERROR, 
                                   function(event) {that_gdata_obj.handleerror(event);});
        air.trace("end callback setup");
        if (this.use_proxy) {
            air.trace("Connecting socket");
            this.sock.connect(this.proxy_host, this.proxy_port);
        } else {
            this.sock.connect('docs.google.com', 80);
        }
        
        air.trace("Connecting now");
    }
}